home *** CD-ROM | disk | FTP | other *** search
/ Aminet 30 / Aminet 30 (1999)(Schatztruhe)[!][Apr 1999].iso / Aminet / dev / lang / SmallEiffel.lha / SmallEiffel / lib_std / dictionary.e < prev    next >
Text File  |  1998-12-22  |  15KB  |  642 lines

  1. -- This file is  free  software, which  comes  along  with  SmallEiffel. This
  2. -- software  is  distributed  in the hope that it will be useful, but WITHOUT 
  3. -- ANY  WARRANTY;  without  even  the  implied warranty of MERCHANTABILITY or
  4. -- FITNESS  FOR A PARTICULAR PURPOSE. You can modify it as you want, provided
  5. -- this header is kept unaltered, and a notification of the changes is added.
  6. -- You  are  allowed  to  redistribute  it and sell it, alone or as a part of 
  7. -- another product.
  8. --          Copyright (C) 1994-98 LORIA - UHP - CRIN - INRIA - FRANCE
  9. --            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr 
  10. --                       http://www.loria.fr/SmallEiffel
  11. --
  12. class DICTIONARY[V,K->HASHABLE]
  13.    --
  14.    -- Associative memory. 
  15.    -- Values of type `V' are stored using Keys of type `K'.
  16.    --
  17.  
  18. inherit 
  19.    ANY 
  20.       redefine is_equal, copy 
  21.       end;
  22.  
  23. creation make, with_capacity
  24.  
  25. feature 
  26.  
  27.    Default_size: INTEGER is 32;
  28.      -- Minimum size for storage in muber of items.
  29.  
  30. feature {DICTIONARY}
  31.    
  32.    keys: FIXED_ARRAY[K];            
  33.      -- Storage for keys of type `K'.
  34.  
  35.    store: FIXED_ARRAY[V]; 
  36.      -- Storage for values of type `V'.
  37.     
  38.    modulus: INTEGER;
  39.      -- To compute a hash value in range [0 .. `modulus'-1].
  40.  
  41.    buckets: FIXED_ARRAY[INTEGER];
  42.      -- Valid index range is always [0 .. `modulus'-1].
  43.      -- Contents is a hash code value in range [0 .. `keys.upper'] to 
  44.      -- acess `keys', `store' and `chain' as well.
  45.     
  46.    chain: FIXED_ARRAY[INTEGER]; 
  47.      -- Used to chain both free slots and hash-code clash.
  48.      -- Value -1 mark the end of a list.
  49.     
  50.    first_free_slot: INTEGER;
  51.      -- First element of the free-slot list or -1 when no more
  52.      -- free slot are available.
  53.  
  54. feature {DICTIONARY}  -- Internal cache handling :
  55.     
  56.    cache_keys_idx: INTEGER;
  57.      -- Contents is -1 or caches the last visited entry using : `has',
  58.      -- `at', `item', or `key'.
  59.  
  60.    cache_user_idx: INTEGER;
  61.      -- Contents is -1 or in range [1 .. `count']. When not -1, it 
  62.      -- caches the last user's index used with `item' or `key'.
  63.  
  64.    cache_buckets_idx: INTEGER;
  65.      -- Contents means nothing when `cache_user_idx' is -1.
  66.      -- Otherwise, gives the current position in `buckets' during 
  67.      -- traversal.
  68.  
  69. feature {NONE}
  70.  
  71.    buckets_keys_ratio: INTEGER is 3;
  72.      -- To compute `modulus' as `ratio' * `capacity'.
  73.  
  74. feature {NONE}
  75.  
  76.    make is
  77.      -- Internal initial storage size of the dictionary is set to
  78.      -- the default `Default_size' value. Then, tuning of needed storage 
  79.      -- size is done automatically according to usage. 
  80.      -- If you are really sure that your dictionary is always really
  81.      -- bigger than `Default_size', you may use `with_capacity' to save some 
  82.      -- execution time.
  83.       do
  84.      with_capacity(Default_size);
  85.       ensure
  86.      empty;
  87.      capacity = Default_size
  88.       end;
  89.    
  90.    with_capacity(medium_size: INTEGER) is
  91.      -- May be used to save some execution time if one is sure 
  92.      -- that storage size will rapidly become really bigger than
  93.      -- `Default_size'. When first `remove' occurs, storage size may 
  94.      -- naturally become smaller than `medium_size'. Afterall, 
  95.      -- tuning of storage size is done automatically according to
  96.      -- usage.
  97.       require
  98.      medium_size > 0
  99.       local
  100.      i: INTEGER;
  101.       do
  102.      !!keys.make(medium_size);
  103.      !!store.make(medium_size);
  104.      modulus := buckets_keys_ratio * medium_size;
  105.      !!buckets.make(modulus);
  106.      buckets.set_all_with(-1);
  107.      from
  108.         !!chain.make(medium_size);
  109.         i := chain.upper;
  110.         first_free_slot := i;
  111.      until
  112.         i < 0
  113.      loop
  114.         chain.put(i - 1, i);
  115.         i := i - 1;
  116.      end;
  117.      cache_keys_idx := -1;
  118.      cache_user_idx := -1;
  119.      count := 0;
  120.       ensure
  121.      empty;
  122.      capacity = medium_size
  123.       end;
  124.  
  125. feature -- Counting :
  126.  
  127.    count: INTEGER;
  128.      -- Actual `count' of stored elements.
  129.  
  130.    empty: BOOLEAN is
  131.       do
  132.      Result := count = 0;
  133.       ensure
  134.      Result = (count = 0)
  135.       end;
  136.       
  137. feature -- Basic access :
  138.  
  139.    has(k: K): BOOLEAN is
  140.      -- Is there an item currently associated with key `k' ?
  141.       do
  142.      if cache_keys_idx < 0 or else 
  143.         not k.is_equal(keys.item(cache_keys_idx))
  144.       then
  145.         cache_user_idx := -1;
  146.         from
  147.            cache_keys_idx := buckets.item(k.hash_code \\ modulus);
  148.         until
  149.            cache_keys_idx < 0 or else
  150.            k.is_equal(keys.item(cache_keys_idx))
  151.         loop
  152.            cache_keys_idx := chain.item(cache_keys_idx);
  153.         end;
  154.      end;
  155.      Result := (cache_keys_idx >= 0);
  156.       end;
  157.    
  158.    at, infix "@" (k: K): V is
  159.      -- Return the item stored at key `k'.
  160.       require
  161.      has(k)
  162.       do
  163.      if cache_keys_idx < 0 or else 
  164.         not k.is_equal(keys.item(cache_keys_idx))
  165.       then
  166.         cache_user_idx := -1;
  167.         from
  168.            cache_keys_idx := buckets.item(k.hash_code \\ modulus);
  169.         until
  170.            k.is_equal(keys.item(cache_keys_idx))
  171.         loop
  172.            cache_keys_idx := chain.item(cache_keys_idx);
  173.         end;
  174.      end;
  175.      Result := store.item(cache_keys_idx);
  176.       end;
  177.    
  178. feature -- The only way to add or to change an entry :
  179.  
  180.    put(v: V; k: K) is
  181.      -- If there is as yet no key `k' in the dictionary, enter 
  182.      -- it with item `v'. Otherwise overwrite the item associated
  183.      -- with key `k'.
  184.       local
  185.      h: INTEGER;
  186.       do
  187.      if cache_keys_idx < 0 or else 
  188.         not k.is_equal(keys.item(cache_keys_idx)) 
  189.       then
  190.         cache_user_idx := -1;
  191.         from
  192.            h := k.hash_code \\ modulus;
  193.            cache_keys_idx := buckets.item(h);
  194.         until
  195.            cache_keys_idx < 0 or else 
  196.            k.is_equal (keys.item (cache_keys_idx))
  197.         loop
  198.            cache_keys_idx := chain.item (cache_keys_idx);
  199.         end;
  200.         if cache_keys_idx < 0 then
  201.            if first_free_slot < 0 then
  202.           expand;
  203.           h := k.hash_code \\ modulus;
  204.            end;
  205.            keys.put(k,first_free_slot);
  206.            store.put(v,first_free_slot);
  207.            cache_keys_idx := first_free_slot;
  208.            first_free_slot := chain.item(first_free_slot);
  209.            chain.put(buckets.item(h),cache_keys_idx);
  210.            buckets.put(cache_keys_idx,h);
  211.            count := count + 1;
  212.         else
  213.            store.put(v,cache_keys_idx);
  214.         end;
  215.      else
  216.         store.put(v,cache_keys_idx);
  217.      end;
  218.       ensure
  219.      v = at(k)
  220.       end;
  221.  
  222. feature -- Looking and Searching :
  223.  
  224.    nb_occurrences(v: V): INTEGER is
  225.      -- Number of occurrences using `equal'.
  226.      -- See also `fast_nb_occurrences' to chose
  227.      -- the apropriate one.
  228.       local
  229.      i: INTEGER;
  230.       do
  231.      from
  232.         i := 1
  233.      until
  234.         i > count
  235.      loop
  236.         if equal_like(v,item(i)) then
  237.            Result := Result + 1;
  238.         end;
  239.         i := i + 1;
  240.      end;
  241.       ensure
  242.      Result >= 0
  243.       end;
  244.       
  245.    fast_nb_occurrences(v: V): INTEGER is
  246.      -- Number of occurrences using `='.
  247.       local
  248.      i: INTEGER;
  249.       do
  250.      from
  251.         i := 1
  252.      until
  253.         i > count
  254.      loop
  255.         if v = item(i) then
  256.            Result := Result + 1;
  257.         end;
  258.         i := i + 1;
  259.      end;
  260.       ensure
  261.      Result >= 0;
  262.       end;
  263.  
  264.    key_at(v: V): K is
  265.      -- Retrieve the key used for value `v' using `equal' for comparison. 
  266.       require
  267.      nb_occurrences(v) = 1
  268.       local
  269.      i: INTEGER;
  270.       do
  271.      from  
  272.         i := 1;
  273.      until
  274.         equal_like(v,item(i))
  275.      loop
  276.         i := i + 1;
  277.      end;
  278.      Result := keys.item(cache_keys_idx);
  279.       ensure
  280.      equal(at(Result),v)
  281.       end;
  282.    
  283.    fast_key_at(v: V): K is
  284.      -- Retrieve the key used for value `v' using `=' for comparison. 
  285.       require
  286.      fast_nb_occurrences(v) = 1
  287.       local
  288.      i: INTEGER;
  289.       do
  290.      from  
  291.         i := 1;
  292.      until
  293.         v = item(i)
  294.      loop
  295.         i := i + 1;
  296.      end;
  297.      Result := keys.item(cache_keys_idx);
  298.       ensure
  299.      at(Result) = v
  300.       end;
  301.  
  302.    capacity: INTEGER is
  303.       do
  304.      Result := keys.count;
  305.       end;
  306.  
  307. feature -- Removing :
  308.  
  309.    remove(k: K) is
  310.       local
  311.      h, keys_idx, keys_next_idx: INTEGER;
  312.       do
  313.      h := k.hash_code \\ modulus;
  314.      keys_idx := buckets.item(h);
  315.      if keys_idx < 0 then
  316.      elseif keys.item(keys_idx).is_equal(k) then
  317.         buckets.put(chain.item(keys_idx),h);
  318.         chain.put(first_free_slot,keys_idx);
  319.         first_free_slot := keys_idx;
  320.         cache_user_idx := -1;
  321.         cache_keys_idx := -1;
  322.         count := count - 1;
  323.      else
  324.         from
  325.            keys_next_idx := chain.item(keys_idx);
  326.         until
  327.            keys_next_idx < 0 or else
  328.            keys.item(keys_next_idx).is_equal(k)
  329.            loop
  330.           keys_idx := keys_next_idx;
  331.           keys_next_idx := chain.item(keys_next_idx);
  332.            end;
  333.            if keys_next_idx >= 0 then
  334.           chain.put(chain.item(keys_next_idx),keys_idx);
  335.           chain.put(first_free_slot,keys_next_idx);
  336.           first_free_slot := keys_next_idx;
  337.           cache_user_idx := -1;
  338.           cache_keys_idx := -1;
  339.           count := count - 1;
  340.            end;
  341.         end;
  342.       ensure
  343.      not has(k)
  344.       end;
  345.  
  346.    clear is
  347.      -- Discard all items.
  348.       local
  349.      i: INTEGER;
  350.       do
  351.      buckets.set_all_with(-1);
  352.      from
  353.         i := chain.upper;
  354.         first_free_slot := i;
  355.      until
  356.         i < 0
  357.      loop
  358.         chain.put(i - 1, i);
  359.         i := i - 1;
  360.      end;
  361.      cache_keys_idx := -1;
  362.      cache_user_idx := -1;
  363.      count := 0;
  364.       ensure
  365.      empty;
  366.       end;
  367.  
  368. feature -- To provide iterating facilities :
  369.  
  370.    lower: INTEGER is 1;
  371.  
  372.    upper: INTEGER is
  373.       do
  374.      Result := count;
  375.       ensure
  376.      Result = count
  377.       end;
  378.  
  379.    valid_index(idx: INTEGER): BOOLEAN is
  380.       do
  381.      Result := (1 <= idx) and then (idx <= count);
  382.       ensure
  383.      Result =  (1 <= idx and then idx <= count);
  384.       end;
  385.    
  386.    item(idx: INTEGER): V is
  387.       require
  388.      valid_index(idx)
  389.       do
  390.      set_cache_user_idx(idx);
  391.      Result := store.item(cache_keys_idx);
  392.       ensure
  393.      Result = at(key(idx))
  394.       end;
  395.    
  396.    key(idx: INTEGER): K is
  397.       require
  398.      valid_index(idx)
  399.       do
  400.      set_cache_user_idx(idx);
  401.      Result := keys.item(cache_keys_idx);
  402.       ensure
  403.      at(Result) = item(idx)
  404.       end;
  405.  
  406. feature
  407.    
  408.    is_equal(other: like current): BOOLEAN is
  409.       local
  410.      buckets_idx, keys_idx: INTEGER;
  411.      k: K;
  412.      v1, v2: V;
  413.       do
  414.      if Current = other then
  415.         Result := true;
  416.      elseif count = other.count then
  417.         from
  418.            Result := true;
  419.            buckets_idx := 0;
  420.         until
  421.            not Result or else buckets_idx > buckets.upper
  422.         loop
  423.            keys_idx := buckets.item(buckets_idx); 
  424.            if keys_idx >= 0 then
  425.           from
  426.           until
  427.              not Result or else keys_idx < 0
  428.           loop
  429.              k := keys.item(keys_idx);
  430.              if other.has(k) then
  431.             v1 := store.item(keys_idx);
  432.             v2 := other.at(k);
  433.             Result := equal_like(v1,v2);
  434.              else
  435.             Result := false;
  436.              end;
  437.              keys_idx := chain.item(keys_idx);
  438.           end;
  439.            end;
  440.            buckets_idx := buckets_idx + 1;
  441.         end;
  442.      end;
  443.       end;
  444.  
  445.    copy(other: like current) is
  446.       do
  447.      count := other.count;
  448.      modulus := other.modulus;
  449.      first_free_slot := other.first_free_slot;
  450.      cache_keys_idx := other.cache_keys_idx;
  451.      cache_user_idx := other.cache_user_idx;
  452.      cache_buckets_idx := other.cache_buckets_idx;
  453.      if buckets = Void then
  454.         buckets := other.buckets.twin;
  455.         keys := other.keys.twin;
  456.         store := other.store.twin;
  457.         chain := other.chain.twin;
  458.      else
  459.         buckets.copy(other.buckets);
  460.         keys.copy(other.keys);
  461.         store.copy(other.store);
  462.         chain.copy(other.chain);
  463.      end;
  464.       end;
  465.  
  466. feature {NONE} 
  467.    
  468.    expand is
  469.      -- The dictionary must grow.
  470.       local
  471.      i: INTEGER;
  472.       do
  473.      from
  474.         i := keys.count;
  475.         resize_buckets(i * 2 * buckets_keys_ratio);
  476.      until
  477.         i = 0
  478.      loop
  479.         chain.add_last(first_free_slot);
  480.         first_free_slot := chain.upper;
  481.         i := i - 1;
  482.      end;
  483.      keys.resize(chain.count);
  484.      store.resize(chain.count);
  485.       ensure
  486.      first_free_slot >= 0
  487.       end;
  488.  
  489.    resize_buckets(new_modulus: INTEGER) is
  490.       local
  491.      h, i: INTEGER;
  492.       do
  493.      modulus := new_modulus;
  494.      buckets.resize(new_modulus);
  495.      buckets.set_all_with(-1);
  496.      from
  497.      until
  498.         first_free_slot < 0
  499.      loop
  500.         i := chain.item(first_free_slot);
  501.         chain.put(-2,first_free_slot);
  502.         first_free_slot := i;
  503.      end;
  504.      check 
  505.         first_free_slot = -1;
  506.      end;
  507.      from
  508.         i := chain.upper;
  509.      until
  510.         i < 0
  511.      loop
  512.         if chain.item(i) = -2 then
  513.            chain.put(first_free_slot,i);
  514.            first_free_slot := i;
  515.         else
  516.            h := keys.item(i).hash_code \\ new_modulus;
  517.            chain.put(buckets.item(h),i);
  518.            buckets.put(i,h);
  519.         end;
  520.         i := i - 1;
  521.      end;
  522.       end;
  523.  
  524. feature {NONE}
  525.  
  526.    equal_like(v1, v2: V): BOOLEAN is
  527.      -- Note: to avoid calling `equal' :-(
  528.      -- Because SmallEiffel is not yet able to infer 
  529.      -- arguments types.
  530.       do
  531.      if v1.is_expanded_type then
  532.         Result := v1 = v2 or else v1.is_equal(v2);
  533.      elseif v1 = v2 then
  534.         Result := true;
  535.      elseif v1 = Void or else v2 = Void then
  536.      else
  537.         Result := v1.is_equal(v2);
  538.      end;
  539.       end;
  540.  
  541. feature {NONE}
  542.  
  543.    set_cache_user_idx(idx: INTEGER) is
  544.       require
  545.      valid_index(idx)
  546.       local
  547.      i: INTEGER;
  548.       do
  549.      if idx = cache_user_idx + 1 then
  550.         cache_user_idx := idx;
  551.         if chain.item(cache_keys_idx) < 0 then
  552.            from
  553.           cache_buckets_idx := cache_buckets_idx + 1;
  554.            until
  555.           buckets.item(cache_buckets_idx) >= 0
  556.            loop
  557.           cache_buckets_idx := cache_buckets_idx + 1;
  558.            end;
  559.            cache_keys_idx := buckets.item(cache_buckets_idx);
  560.         else
  561.            cache_keys_idx := chain.item(cache_keys_idx);
  562.         end;
  563.      elseif idx = cache_user_idx - 1 then
  564.         cache_user_idx := idx;
  565.         if cache_keys_idx = buckets.item(cache_buckets_idx) then
  566.            from
  567.           cache_buckets_idx := cache_buckets_idx - 1;
  568.            until
  569.           buckets.item(cache_buckets_idx) >= 0
  570.            loop
  571.           cache_buckets_idx := cache_buckets_idx - 1;
  572.            end;
  573.            from
  574.           cache_keys_idx := buckets.item(cache_buckets_idx);
  575.            until
  576.           chain.item(cache_keys_idx) < 0
  577.            loop
  578.           cache_keys_idx := chain.item(cache_keys_idx);
  579.            end;
  580.         else
  581.            from
  582.           i := buckets.item(cache_buckets_idx);
  583.            until
  584.           chain.item(i) = cache_keys_idx
  585.            loop
  586.           i := chain.item(i);
  587.            end;
  588.            cache_keys_idx := i;
  589.         end;
  590.      elseif idx = cache_user_idx then
  591.      elseif idx = 1 then
  592.         cache_user_idx := 1;
  593.         from
  594.            cache_buckets_idx := 0;
  595.         until
  596.            buckets.item(cache_buckets_idx) >= 0
  597.         loop
  598.            cache_buckets_idx := cache_buckets_idx + 1;
  599.         end;
  600.         cache_keys_idx := buckets.item(cache_buckets_idx);
  601.      elseif idx = count then
  602.         cache_user_idx := idx;
  603.         from
  604.            cache_buckets_idx := buckets.upper;
  605.         until
  606.            buckets.item(cache_buckets_idx) >= 0
  607.         loop
  608.            cache_buckets_idx := cache_buckets_idx - 1;
  609.         end;
  610.         from
  611.            cache_keys_idx := buckets.item(cache_buckets_idx);
  612.         until
  613.            chain.item(cache_keys_idx) < 0
  614.         loop
  615.            cache_keys_idx := chain.item(cache_keys_idx);
  616.         end;
  617.      else
  618.         from 
  619.            set_cache_user_idx(1);
  620.         until
  621.            cache_user_idx = idx
  622.         loop
  623.            set_cache_user_idx(cache_user_idx + 1);
  624.         end;
  625.      end;
  626.       ensure
  627.      cache_user_idx = idx;
  628.      buckets.valid_index(cache_buckets_idx);
  629.      keys.valid_index(cache_keys_idx);
  630.       end;
  631.    
  632. invariant
  633.  
  634.    (keys.upper = store.upper) and (store.upper = chain.upper);
  635.  
  636.    buckets.upper = modulus - 1;
  637.  
  638.    -1 <= first_free_slot and first_free_slot <= chain.upper;
  639.    
  640. end -- DICTIONARY[V,K->HASHABLE]
  641.  
  642.